/**
 * Axios HTTP 封装：内置拦截器、Token 注入与类型友好的请求助手。
 * baseURL 来自 VITE_API_BASE_URL，未配置则回退为 '/api'。
 */
import axios, { type AxiosInstance, type AxiosRequestConfig, type AxiosResponse } from 'axios'

/**
 * 统一的接口返回结构（兼容常见服务约定）：
 * - success：显式标记是否成功
 * - code：业务状态码（0/200 视为成功）
 * - data：实际数据载荷
 * - message/msg：人类可读的提示
 */
export interface ApiResponse<T = unknown> {
  code?: number | string
  data: T
  message?: string
  msg?: string
  success?: boolean
}

/** 所有请求的默认基础路径。 */
const baseURL = '/api'

/** 预配置的 Axios 实例。 */
const http: AxiosInstance = axios.create({
  baseURL,
  timeout: 15000,
  withCredentials: false,
})

/**
 * 请求拦截器：
 * - 从 localStorage.token 注入 Authorization Bearer 令牌（若请求头未显式设置）。
 */
http.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('token')
    if (token && config.headers) {
      const hasAuth =
        typeof config.headers.Authorization === 'string' && config.headers.Authorization.length > 0
      if (!hasAuth) {
        config.headers.Authorization = `Bearer ${token}`
      }
    }
    return config
  },
  (error) => Promise.reject(error),
)

/** 从响应载荷中提取可读消息（若存在）。 */
function extractMessage(payload: unknown): string | undefined {
  if (!payload || typeof payload !== 'object') return undefined
  const obj = payload as Record<string, unknown>
  const candidates = ['message', 'msg', 'error', 'detail']
  for (const key of candidates) {
    const value = obj[key]
    if (typeof value === 'string' && value.trim().length > 0) {
      return value
    }
  }
  return undefined
}

/** 将 HTTP 状态码转换为本地化错误信息。 */
function httpStatusMessage(status: number): string {
  switch (status) {
    case 400:
      return '请求参数错误'
    case 401:
      return '未授权或登录已过期'
    case 403:
      return '无权限访问'
    case 404:
      return '资源未找到'
    case 408:
      return '请求超时'
    case 500:
      return '服务器错误'
    case 502:
      return '网关错误'
    case 503:
      return '服务不可用'
    case 504:
      return '网关超时'
    default:
      return `请求错误（${status}）`
  }
}

/**
 * 响应拦截器：
 * - 当 success === true 或 code ∈ {0, 200} 时直接返回 data
 * - 若存在 code 且表示失败，则使用提取的 message 拒绝
 * - 统一网络/服务端错误为可读信息
 */
http.interceptors.response.use(
  (response: AxiosResponse<any, any, {}>): Promise<any> | any => {
    const { data, status } = response
    if (status >= 200 && status < 300) {
      if (data && typeof data === 'object') {
        const code = data.code as number | string | undefined
        const success = data.success
        if (success === true) return (data.data as unknown) ?? (data as unknown)
        if (code === 0 || code === 200 || code === '0' || code === '200') {
          return (data.data as any) ?? (data as any)
        }
        if (typeof code === 'undefined') {
          return data as any
        }
        const message = extractMessage(data) || '请求失败'
        return Promise.reject(new Error(message))
      }
      return response.data as any
    }
    return Promise.reject(new Error('网络错误'))
  },
  (error) => {
    if (axios.isCancel(error)) return Promise.reject(error)
    if (error?.response) {
      const status = error.response.status as number
      const msg = (extractMessage(error.response.data) || httpStatusMessage(status)) ?? '请求出错'
      return Promise.reject(new Error(msg))
    }
    const message = error?.message || '网络异常，请检查您的网络连接'
    return Promise.reject(new Error(message))
  },
)

/** 与 AxiosRequestConfig 等价，用于保留下方助手的范型。 */
type RequestConfig<T = unknown> = AxiosRequestConfig<T>

/** 低级请求助手，保留范型返回类型。 */
export function request<T = unknown>(config: RequestConfig): Promise<T> {
  return http.request<unknown, T>(config)
}

/** GET 快捷方法。 */
export const get = <T = unknown, R = T>(url: string, config?: RequestConfig) =>
  http.get<R>(url, config)

/** POST 快捷方法。 */
export const post = <T = unknown, R = T>(url: string, data?: T, config?: RequestConfig) =>
  http.post<R>(url, data, config)

/** PUT 快捷方法。 */
export const put = <T = unknown, R = T>(url: string, data?: T, config?: RequestConfig) =>
  http.put<R>(url, data, config)

/** DELETE 快捷方法。 */
export const del = <T = unknown, R = T>(url: string, config?: RequestConfig) =>
  http.delete<R>(url, config)

export default http
